#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest/doctest.h"

#include "klib/utils.hpp"
#include <cassert>

using namespace klib;
using namespace std::string_literals;
using namespace std::string_view_literals;

TEST_CASE("utils") {

    SUBCASE("next_pow_of_2") {
        CHECK(1024 == klib::next_pow_of_2(1023u));
        auto n1 = klib::next_pow_of_2((uint64_t) 0xffff'ffffull);
        CHECK_EQ(0x1'0000'0000ull, n1);
        auto n2 = klib::next_pow_of_2((uint64_t) 0xffff'fff8ull);
        CHECK_EQ(0x1'0000'0000ull, n2);
    }

    SUBCASE("data_ptr_t") {
        char buf[3];
        data_ptr_t ptr;
        CHECK(ptr.ptr == nullptr);
        data_ptr_t p(buf, sizeof(buf));
        CHECK(p.cap == 3);
        CHECK(p.ptr == buf);
        ptr = p;
        CHECK(ptr.cap == 3);
        CHECK(ptr.ptr == buf);
        buf_ptr_t b1(buf, sizeof(buf));
        CHECK_EQ(3, b1.cap);
        CHECK_EQ(0, b1.rpos);
        CHECK_EQ(0, b1.wpos);
        CHECK_EQ(buf, b1.ptr);
        buf_ptr_t b2 = b1;
        CHECK_EQ(3, b2.cap);
        CHECK_EQ(buf, b2.ptr);
    }

    SUBCASE("buffer_t") {
        char buf[256] = {};
        data_ptr_t data(buf, 0);
        {
            buffer_t<4> buf(&data, +[](data_ptr_t* arg, const char* data, size_t size) {
                std::memcpy(arg->ptr + arg->cap, data, size);
                arg->cap += size;
            });
            buf.append("kiven", 5);
            buf.append(" lee", 4);
            buf.push_back(' ', '1', '2', '3', '4');
            buf.push_back('5');
        }
        CHECK("kiven lee 12345"sv == (string_view) data);
    }

    SUBCASE("format_use_append") {
        time_t t = 1657390330; // 2022-07-10 02:12:10
        tm tm_ = *localtime(&t);
        std::string a1("kiven");
        std::string s = "2022-07-10 02:12:10, kiven";
        fmt::fmt_arg_t args[] = {tm_, a1};
        std::string ret;
        size_t size = fmt::vformat(ret, "{}, {}", args);
        CHECK(size == s.size());
        CHECK(s == ret);
        size = fmt::format(ret, "{}", "lee");
        CHECK(size == 3);
        s.append("lee");
        CHECK(s == ret);
    }

    SUBCASE("string_helper") {
        const char* ss[] = {"a", "嗨", "𠮷", "💩"};
        CHECK(u8_length(*ss[0]) == std::strlen(ss[0]));
        CHECK(u8_length(*ss[1]) == std::strlen(ss[1]));
        CHECK(u8_length(*ss[2]) == std::strlen(ss[2]));
        CHECK(u8_length(*ss[3]) == std::strlen(ss[3]));
        const char ss2[] = "a嗨𠮷💩";
        CHECK(u8_strlen(ss2) == 4);
        
        CHECK('e' == to_hex<0>(0xe3));
        CHECK('3' == to_hex<1>(0xe3));
        CHECK(0xe3 == from_hex('e', '3'));
        
        char s3[] = "www.baidu.com";
        str_replace_all(s3, '.', '|');
        CHECK("www|baidu|com"sv == s3);
        char s4[] = "www.baidu.com";
        str_replace_all(s4, sizeof(s4), '.', '|');
        CHECK("www|baidu|com"sv == s4);

        struct ss_t { cp_char_t* ss; size_t ss_len; size_t index; };
        auto sp_fn = [] (void* arg, size_t i, cp_char_t data, size_t size) {
            auto ss = (ss_t*) arg;
            INFO("split char, i = " << i);
            CHECK_EQ(ss->index, i);
            ++ss->index;
            CHECK_LT(i, ss->ss_len);
            CHECK_EQ(string(ss->ss[i]), string_view(data, size));
        };

        char s5[] = "  , ab cd ,  ef , , g,h , 6 . 9 ,,  ";

        const char* s6[] = {"", "ab cd", "ef", "", "g", "h", "6 . 9", ""};
        ss_t ss6{s6, std::size(s6), 0};
        str_split(s5, sizeof(s5) - 1, ',', &ss6, sp_fn);
        CHECK_EQ(ss6.index, std::size(s6));

        const char* s7[] = {"", "ab cd", "ef", "", "g", "h", "6", "9", ""};
        ss_t ss7{s7, std::size(s7), 0};
        str_split(s5, sizeof(s5) - 1, ",.", &ss7, sp_fn);
        CHECK_EQ(ss7.index, std::size(s7));
        
        char s8[] = "   ab cd ,  ef , , g,h , 6 . 9";
        const char* s9[] = {"ab cd", "ef", "", "g", "h", "6 . 9"};
        ss_t ss9{s9, std::size(s9), 0};
        str_split(s8, sizeof(s8) - 1, ',', &ss9, sp_fn);
        CHECK_EQ(ss9.index, std::size(s9));
    }

    SUBCASE("strcopy") {
        char s[] = "01234567890abcdefghijklmn";
        for (size_t i = 0; i < sizeof(s); ++i) {
            char d[32] = {};
            auto de = strcopy(d, s + i);
            INFO("i = " << i);
            CHECK_EQ(de, d + sizeof(s) - i - 1);
            CHECK_EQ(*de, '\0');
            CHECK_EQ(string(s + i), string(d));
        }
    }

    SUBCASE("rand_t") {
        // 随机数测试的最小值/最大值/最大循环次数
        const size_t min = 1, max = 4, rep = 1000;
        rand_t rand(min, max);
        bool rbs[max - min + 1] = {};
        for (size_t i = 0; i < std::size(rbs); ++i)
            REQUIRE_MESSAGE(rbs[i] == false, "i = ", i);

        for (size_t i = 0; i < rep; ++i) {
            auto n = rand.next();
            REQUIRE(n >= min);
            REQUIRE(n <= max);
            rbs[n - min] = true;
            bool b = true;
            for (size_t j = 0; j < std::size(rbs); ++j)
                if (!rbs[j]) { b = false; break; }
            if (b) break;
        }
        for (size_t i = 0; i < std::size(rbs); ++i)
            REQUIRE_MESSAGE(rbs[i], "i = ", i);
    }

    SUBCASE("dstring_t") {
        dstring_t<15> d;
        CHECK_EQ('\0', *d.data());
        CHECK_EQ(0, d.size());
        for (size_t i = 0, n = d.capacity(); i < n; ++i)
            d.data()[i] = 'x';
        CHECK_EQ(""sv, d.to_string());
        CHECK_EQ('x', *d.data());
        d.resize(1, 'b');
        INFO("resize(1, 'b') fail");
        CHECK_EQ(1, d.size());
        CHECK_EQ('b', *d.data());
        CHECK_EQ('x', *d.end());
        CHECK_EQ("b"sv, d.c_str());
        d.putchar('c');
        CHECK_MESSAGE("bc"sv == d.to_string(), "putchar('c') fail");
        d.putchar('d', 'e', 'f', 'g');
        CHECK_MESSAGE("bcdefg"sv == d.to_string_view(), "putchar('d', ...) fail");
        d.append("hijk");
        CHECK_MESSAGE("bcdefghijk"sv == d.to_string(), "append fail");
        d.format(",name={},age={}", "kiven", 18);
        CHECK_MESSAGE("bcdefghijk,name=kiven,age=18"sv == d.to_string(), "format fail");
        d.printf(",sex=%s(%B)", "man", true);
        CHECK_MESSAGE("bcdefghijk,name=kiven,age=18,sex=man(true)"sv == d.to_string(), "printf fail");
        CHECK_EQ(d.size(), 42);
        CHECK_EQ(d.capacity(), next_pow_of_2((size_t)42) - 1);
        d.resize(0);
        CHECK_EQ(""sv, d.to_string());
    }

    SUBCASE("double_str_t") {
        auto ceq = [] (string_view s, double d) {
            CHECK_EQ(s, double_str_t(d).to_string_view());
        };

        CHECK_EQ(64, sizeof(double_str_t));
        CHECK_EQ(string_view(), double_str_t().to_string());
        ceq("0", 0.0);
        ceq("0", 0.0000001);
        ceq("0.0001", 0.0001);
        ceq("0.00001", 0.00001);
        // musl库此测试通不过
        // ceq("0.000001", 0.000001);
        ceq("0.1", 0.1);
        ceq("0.03", 0.03);
        ceq("1.23", 1.23);
        ceq("1.234568", 1.23456789);
    }
}